package com.sailing.xjpb.web.base.converter;

import com.sailing.xjpb.common.SailingConstants;
import com.sailing.xjpb.common.exception.BusinessException;
import com.sailing.xjpb.web.base.configuration.SailingWebMvcConfigurerAdapter;
import lombok.NonNull;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.math.NumberUtils;
import org.springframework.core.MethodParameter;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.domain.Sort.Direction;
import org.springframework.web.bind.support.WebDataBinderFactory;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.method.support.ModelAndViewContainer;

import javax.servlet.http.HttpServletRequest;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

/**
 * 分页参数转换-将请求参数封装为pageable对象 不配置，controller获取分页参数是这样配置的：public xxx
 * xxxMethod( @RequestParam int page, @RequestParam int size)
 * 配置后，controller获取分页参数是这样配置的：public xxx xxxMethod(Pageable pageable)
 *
 * @author YW
 * createDate: 2020-12-08
 * @see SailingWebMvcConfigurerAdapter
 */
@Setter
@Slf4j
public class PageableMethodArgumentResolver implements HandlerMethodArgumentResolver {
    /***
     * 默认最大：1w
     */
    private static final int DEFAULT_MAX_PAGE_SIZE = 10000;
    /**
     * 分页参数名称
     * -- SETTER --
     * 分页参数名称
     */
    private String paramPage = SailingConstants.PARAM_PAGE;
    /**
     * 每页大小参数名称
     * -- SETTER --
     * 每页大小参数名称
     */
    private String paramSize = SailingConstants.PARAM_SIZE;
    /**
     * 排序字段参数名称
     * -- SETTER --
     * 排序字段参数名称
     */
    private String paramSort = SailingConstants.PARAM_SORT;
    /**
     * 排序顺序参数名称
     * -- SETTER --
     * 排序顺序参数名称
     */
    private String paramOrder = SailingConstants.PARAM_ORDER;

    /**
     * 空值排序参数名称
     * -- SETTER --
     * 空值排序参数名称
     */
    private String paramNullHandling = "nullHandling";

    @Override
    public boolean supportsParameter(MethodParameter parameter) {
        // 不处理附件参数-附件参数交由平台处理
        return Pageable.class.isAssignableFrom(parameter.getParameterType());
    }

    @Override
    public Object resolveArgument(@NonNull MethodParameter parameter, ModelAndViewContainer mavContainer,
                                  NativeWebRequest webRequest, WebDataBinderFactory binderFactory) {
        HttpServletRequest request = webRequest.getNativeRequest(HttpServletRequest.class);
        if (request == null) {
            return PageRequest.of(0, 10);
        }
        // 分页
        String pageStr = request.getParameter(paramPage);
        String sizeStr = request.getParameter(paramSize);
        // 排序字段
        String sortStr = request.getParameter(paramSort);
        // 排序顺序 ASC|DESC
        String orderStr = request.getParameter(paramOrder);
        // 排序顺序  NATIVE, NULLS_FIRST, NULLS_LAST
        String nullHandlingStr = request.getParameter(paramNullHandling);
        if (log.isDebugEnabled()) {
            log.debug("获取到分页参数：page={}，size={}，sort={}，order={}, nullHandling={}", pageStr, sizeStr, sortStr,
                    orderStr, nullHandlingStr);
        }

        if (!NumberUtils.isCreatable(pageStr)) {
            pageStr = "1";
            sizeStr = "10";
            log.warn("分页参数page为空.将使用默认参数page=1");
        }

        if (!NumberUtils.isCreatable(sizeStr)) {
            sizeStr = "10";
            log.warn("分页参数size为空.将使用默认参数size=10");
        }

        int page = Integer.parseInt(pageStr);
        int size = Integer.parseInt(sizeStr);

        if (page < 1) {
            page = 1;
        }

        if (size > DEFAULT_MAX_PAGE_SIZE) {
            size = DEFAULT_MAX_PAGE_SIZE;
            log.warn("分页参数size大于默认最大size.将使用默认DEFAULT_MAX_PAGE_SIZE=" + DEFAULT_MAX_PAGE_SIZE);
        }

        if (StringUtils.isEmpty(sortStr)) {
            return PageRequest.of(page - 1, size);
        }
        Direction direction = orderStr != null ? Direction.fromString(orderStr) : Direction.ASC;
        Sort.NullHandling nullHandling = getNullHandling(nullHandlingStr);
        List<Sort.Order> orders = Arrays.stream(sortStr.split(","))
                .map(s -> new Sort.Order(direction, s, nullHandling))
                .collect(Collectors.toList());
        return PageRequest.of(page - 1, size, Sort.by(orders));

    }

    /**
     * 获取空值排序策略
     *
     * @param nullHandling 空值排序策略
     * @return 空值排序策略枚举
     */
    private Sort.NullHandling getNullHandling(String nullHandling) {
        try {
            if (StringUtils.isEmpty(nullHandling)) {
                return Sort.NullHandling.NATIVE;
            }
            return Sort.NullHandling.valueOf(nullHandling.toUpperCase());
        } catch (IllegalArgumentException e) {
            throw BusinessException.businessError("非法的空值排序策略=" + nullHandling);
        }
    }
}
